<#
Copyright 2012 Chris Duck

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
#>


function GetPackagesFromServer {
	param(
		[Parameter(Mandatory=$false)]
		[ValidateNotNullOrEmpty()]
		[string]$Source,
		
		[Parameter(Mandatory=$false)]
		[ValidateNotNullOrEmpty()]
		[string]$Id,
		
		[Parameter(Mandatory=$false)]
		[ValidateNotNullOrEmpty()]
		[Version]$Version,
		
		[Parameter(Mandatory=$false)]
		[switch]$IncludePrerelease,
		
		[Parameter(Mandatory=$false)]
		[switch]$AllVersions
	)
	if([string]::IsNullOrEmpty($Source)) {
		$SourceObject = Get-NuGetSource -Default
		$SourceURL = $SourceObject.Uri
	} else {
		$SourceObject = Get-NuGetSource -Name $Source
		$SourceURL = $SourceObject.Uri
	}
	if(!$SourceURL) {
		if($Source) {
			throw "Unable to locate source $Source"
		} else {
			throw "No source was specified and no default source exists."
		}
	}
	#Set a page size, at the time 30 was what "NuGet.exe list" used
	$PageSize = 30
	
	#Build an oData context
	$NuGetServer = New-Object NuGet.Server.DataServices.PackageContext($SourceURL)
	
	#Set this to ignore missing properties to increase our static dll's compatibility with the
	#  seemingly randomly changing properties available in NuGet.Server
	$NuGetServer.IgnoreMissingProperties = $true
	
	#Initialize our page and filter 
	$Page=0
	$Filter = @()
	
	#Build the filter based on the parameters that were specified
	if(!$AllVersions -and !$Version) {
		$Filter += "IsLatestVersion"
	}
	if($Id) {
		$Filter += "tolower(Id) eq tolower('$Id')"
	}
	if($Version) {
		$Filter += "Version eq '$($Version.ToString())'"
	}
	
	#Join all the valid filter parts together with AND
	$FilterString = [String]::Join(" and ", $Filter)
	Write-Debug "GetPackagesFromServer: Filter = $FilterString"
	
	$Packages = New-Object Collections.ArrayList

	do {
		#Get a blank query object that will return Package objects from the oData server
		$Query = $NuGetServer.Packages
		
		#Build the query up by adding new options
		if(![String]::IsNullOrEmpty($FilterString)) {
			$Query = $Query.AddQueryOption('$filter', $FilterString)
		}
		$Query = $Query.AddQueryOption('$orderby', "Id")
		$Query = $Query.AddQueryOption('$top', $PageSize)
		$Query = $Query.AddQueryOption('$skip', ($Page * $PageSize))
		$Query = $Query.AddQueryOption('includePrerelease', $IncludePrerelease)
		
		#Run the query and save the results
		$PackageBatchCount = 0
		$Query | %{
			#Save the uri to download this package.
			#  we have to do this here because we need to use the same 
			#  $NuGetServer context that created the entity.
			$_.ReadStreamUri = $NuGetServer.GetReadStreamUri($_)
			
			#Save the source object for this package.
			$_.Source = new-object NuGet.Server.DataServices.Source($SourceObject.Name, $SourceObject.Uri, $SourceObject.Default)
			
			#Add package to the output list
			$Packages.Add($_) | Out-Null
			$PackageBatchCount++
		} 

		Write-Debug ("Page: {0}, Packages: {1}" -f $Page, $PackageBatchCount)
		$Page++
	} while ($PackageBatchCount -eq $PageSize)
	
	if(!$AllVersions) {
		$Packages | Group-Object -Property Id | %{
			$_.Group | Sort-Object -Property Version -Descending | Select-Object -First 1
		}
	} else {
		$Packages
	}
}

function DownloadPackageFromServer {
	param(
		[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
		[ValidateNotNullOrEmpty()]
		$PSPackage,
		
		[Parameter(Mandatory=$true)]
		[ValidateNotNullOrEmpty()]
		[IO.DirectoryInfo]$PackageRepositoryPath
	)
	begin {
		$WebClient = New-Object Net.WebClient
	}
	process {
		#Make sure there is a URI to download from before we do anything
		if([string]::IsNullOrEmpty($PSPackage.ReadStreamUri)) {
			throw "Unable to download package $($PSPackage.Id), ReadStreamUri is empty!"
		}
		
		$PackageFolder = Join-Path $PackageRepositoryPath.FullName $PSPackage.Id
		
		if( !(Test-Path $PackageFolder) ) {
			Write-Debug "DownloadPackageFromServer: Creating local package folder at $PackageFolder"
			if( !(mkdir -Path $PackageFolder -Force) ) {
				throw "Unable to create local package path at $PackageFolder"
			}
		}
		
		$PackageFileName = "{0}.{1}.nupkg" -f $PSPackage.Id, $PSPackage.Version.ToString()
		$PackagePath = Join-Path $PackageFolder $PackageFileName
		
		Write-Debug "DownloadPackageFromServer: Package path will be $PackagePath"
		try {
			Write-Debug "DownloadPackageFromServer: Downloading $PackagePath from $($PSPackage.ReadStreamUri)"
			$WebClient.DownloadFile($PSPackage.ReadStreamUri, $PackagePath)
			return $PackageFileName
			Write-Warning "DownloadPackageFromServer: Package Verification not implemented!"
		} catch {
			throw New-Object Exception("Error downloading package from $($PSPackage.ReadStreamUri). $_", $_.Exception)
		}
	}
	end {
		$WebClient.Dispose()
	}
}